﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text.RegularExpressions;
using com.nm;
using Newtonsoft.Json;
using george.Helper;

// ReSharper disable once CheckNamespace
namespace george.Web.Helper {
    /// <summary>
    /// Url 参数解析类
    /// </summary>
    public static class QueryStringHelper {
        /// <summary>
        /// 获取完整的参数字串（a=1&amp;b=2）
        /// </summary>
        /// <param name="nvc"></param>
        /// <returns></returns>
        public static string GetQuery(this NameValueCollection nvc) {
            var list = (from k in nvc.AllKeys
                        let v = nvc[k]
                        where !string.IsNullOrWhiteSpace(v)
                        select string.Format("{0}={1}",
                            Uri.EscapeUriString(k),
                            Uri.IsWellFormedUriString(v, UriKind.RelativeOrAbsolute)
                                ? v
                                : Uri.EscapeDataString(v)
                            )
                        ).ToList();
            if (list.Count > 1) {
                return list.Join("&");
            }
            return list.Count == 1 ? list[0] : string.Empty;
        }
        /// <summary>
        /// 清除参数
        /// </summary>
        /// <param name="ub"></param>
        /// <returns></returns>
        public static UriBuilder ClearQuery(this UriBuilder ub) {
            ub.Query = string.Empty;
            return ub;
        }

        /// <summary>
        /// 设置参数（如果不经存在则添加）
        /// </summary>
        /// <param name="ub"></param>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public static UriBuilder SetQuery(this UriBuilder ub, string pKey, string pValue) {
            var qs = ub.ParseQuery();
            qs.SetQuery(pKey, pValue);
            ub.Query = qs.GetQuery();
            return ub;
        }
        /// <summary>
        /// 设置参数（如果不经存在则添加）
        /// </summary>
        /// <param name="ub"></param>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public static UriBuilder SetQuery(this UriBuilder ub, string pKey, int pValue) {
            var qs = ub.ParseQuery();
            qs.SetQuery(pKey, pValue);
            ub.Query = qs.GetQuery();
            return ub;
        }
        /// <summary>
        /// 清除参数
        /// </summary>
        /// <param name="nvc"></param>
        /// <returns></returns>
        public static NameValueCollection ClearQuery(this NameValueCollection nvc) {
            nvc.Clear();
            return nvc;
        }

        /// <summary>
        /// 设置参数（如果不经存在则添加）
        /// </summary>
        /// <param name="nvc"></param>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public static NameValueCollection SetQuery(this NameValueCollection nvc, string pKey, string pValue) {
            nvc[pKey] = pValue;
            return nvc;
        }
        /// <summary>
        /// 设置参数（如果不经存在则添加）
        /// </summary>
        /// <param name="nvc"></param>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public static NameValueCollection SetQuery(this NameValueCollection nvc, string pKey, int pValue) {
            nvc[pKey] = pValue.ToString();
            return nvc;
        }
        /// <summary>
        /// 合并参数
        /// </summary>
        /// <param name="nvc"></param>
        /// <param name="query"></param>
        /// <returns></returns>
		public static NameValueCollection CombineQuery(this NameValueCollection nvc, string query) {
            var tmp = ParseQuery(query);
            foreach (var k in tmp.AllKeys) {
                nvc.Set(k, tmp.Get(k));
            }
            return nvc;
        }
        /// <summary>
        /// 解析参数
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="queryString"></param>
        /// <returns></returns>
		public static T ParseQuery<T>(string queryString) where T : new() {
            var obj = ParseQueryToJson(queryString);
            var str = JsonConvert.SerializeObject(obj);
            return JsonConvert.DeserializeObject<T>(str);
        }

        #region ParseQueryToJson
        static object Ppp2(Hashtable table, string name, object value, int level) {
            if (name.StartsWith("[")) {
                var reg = new Regex("\\[(.+?)\\]", RegexOptions.Compiled);
                var ms = reg.Matches(name);
                if (ms.Count <= 0) return table;
                var key = ms[0].Groups[1].Value;
                if (ms.Count > 1) {
                    if (IsNumber(ms[1].Groups[1].Value)) {
                        //Array
                        var arrayName = ms[0].Value + ms[1].Value;
                        var lastPath = name.Substring(arrayName.Length);
                        if (table.ContainsKey(arrayName)) {
                            var arr = (ArrayList)table[arrayName];
                            if (ms.Count == 2) {
                                arr.Add(value);
                            } else {
                                Ppp2(arr[arr.Count - 1] as Hashtable, lastPath, value, level + 1);
                            }
                        } else {
                            var newArr = new ArrayList{
                                ms.Count == 2
                                    ? value
                                    : Ppp2(new Hashtable(), lastPath, value, level + 1)
                            };
                            table.Add(arrayName, newArr);
                        }
                    } else {
                        //Object
                        var objName = ms[0].Groups[1].Value;
                        var lastPath = name.Substring(ms[0].Value.Length);
                        if (table.ContainsKey(objName)) {
                            var obj = (Hashtable)table[objName];
                            Ppp2(obj, lastPath, value, level + 1);
                        } else {
                            var newObj = Ppp2(new Hashtable(), lastPath, value, level + 1);
                            table.Add(objName, newObj);
                        }
                    }
                } else if (ms.Count == 1) {
                    if (!table.ContainsKey(key))
                        table.Add(key, value);
                } else {
                    var lastPath = name.Substring(ms[0].Length);
                    if (!table.ContainsKey(key))
                        table.Add(key, Ppp2(new Hashtable(), lastPath, value, level + 1));
                }
            } else {
                if (table.ContainsKey(name)) {
                    var isA = value.GetType() == typeof(ArrayList);
                    var isAb = table[name].GetType() == typeof(ArrayList);
                    if (isA) {
                        Merge((ArrayList)table[name], (ArrayList)value);
                    } else {
                        if (isAb) {
                            ((ArrayList)table[name]).Add(value);
                        } else {

                            Log.WriteLines(
                                "Level=" + level,
                                "Name=" + name,
                                "T1=" + table[name].GetType().Name,
                                "T2=" + value.GetType().Name,
                                "ParseQueryToJson"
                                );
                        }
                    }
                } else {
                    table.Add(name, value);
                }
            }
            return table;
        }

        static void Merge(ArrayList a, ICollection b) {
            a.AddRange(b);
        }

        static object Merge(IDictionary table) {
            var newTable = new Hashtable();
            foreach (var key in table.Keys) {
                var reg = new Regex("\\[(.+?)\\]", RegexOptions.Compiled);
                var ms = reg.Matches(key.ToString());
                var isTable = table[key].GetType() == typeof(Hashtable);
                var isArray = table[key].GetType() == typeof(ArrayList);
                if (ms.Count > 0) {
                    if (isArray) {
                        var arr = (ArrayList)table[key];
                        if (newTable.ContainsKey(ms[0].Groups[1].Value)) {
                            var newArr = (ArrayList)newTable[ms[0].Groups[1].Value];
                            foreach (var vv in arr) {
                                newArr.Add(vv.GetType() == typeof(Hashtable)
                                    ? Merge((Hashtable)vv)
                                    : vv);
                            }
                        } else {
                            var newArr = new ArrayList();
                            foreach (var vv in arr) {
                                newArr.Add(vv.GetType() == typeof(Hashtable)
                                    ? Merge((Hashtable)vv)
                                    : vv);
                            }
                            newTable.Add(ms[0].Groups[1].Value, newArr);
                        }
                    } else {
                        newTable.Add(ms[0].Groups[1].Value, isTable ? Merge((Hashtable)table[key]) : table[key]);
                    }
                } else {
                    newTable.Add(key, isTable ? Merge((Hashtable)table[key]) : table[key]);
                }
            }
            return newTable;
        }
        #endregion
        
        /// <summary>
        /// 把xx=123&amp;yy=abc的键对数据转换成类似JSON的对象
        /// 注意：纯数字的值会转换成int或decimal类型
        /// </summary>
        /// <param name="queryString"></param>
        /// <returns></returns>
        public static object ParseQueryToJson(string queryString) {
            if (string.IsNullOrWhiteSpace(queryString)) {
                return null;
            }
            while (queryString.StartsWith("?")) {
                queryString = queryString.Substring(1);
            }
            var server = System.Web.HttpContext.Current.Server;
            if (queryString.IndexOf("&", StringComparison.Ordinal) >= 0) {
                var pars = queryString.Split('&');
                var table = new Hashtable();
                foreach (var k in pars) {
                    var resultK = ParseQueryToJson(k);
                    if (resultK == null) continue;
                    var kvp = (KeyValuePair<string, object>)resultK;
                    Ppp2(table, kvp.Key, kvp.Value, 1);
                }
                return Merge(table);
            }
            if (queryString.IndexOf("=", StringComparison.Ordinal) < 0) return queryString;
            var tmp = queryString.Split('=');
            if (tmp.Length != 2 || string.IsNullOrWhiteSpace(tmp[0]) || string.IsNullOrWhiteSpace(tmp[1])) return null;
            var name = server.UrlDecode(tmp[0]);
            // ReSharper disable once PossibleNullReferenceException
            if (name.EndsWith("[]")) {
                name = name.Insert(name.Length - 1, "0");
            }
            var index = name.IndexOf('[');
            if (index > 0) {
                name = "[" + name.Insert(index, "]");
            }
            var result = new KeyValuePair<string, object>(name, ParseValue(server.UrlDecode(tmp[1])));
            return result;
        }

        private static bool IsNumber(string str) {
            return Regex.IsMatch(str, "^\\d+$");
        }
        /// <summary>
        /// 把xx=123&amp;yy=abc的键对数据转换成类似JSON的对象
        /// 注意：纯数字的值会转换成int或decimal类型
        /// </summary>
        /// <param name="queryString"></param>
        /// <returns></returns>
        public static object ParseQueryToJson2(string queryString) {
            if (string.IsNullOrWhiteSpace(queryString)) {
                return null;
            }
            while (queryString.StartsWith("?")) {
                queryString = queryString.Substring(1);
            }
            var server = System.Web.HttpContext.Current.Server;
            if (queryString.IndexOf("&", StringComparison.Ordinal) >= 0) {
                var pars = queryString.Split('&');
                var table = new Hashtable();
                foreach (var k in pars) {
                    KeyValuePair<string, object> kvp;
                    try {
                        kvp = (KeyValuePair<string, object>)ParseQueryToJson2(k);
                    } catch {
                        continue;
                    }
                    var obj = table[kvp.Key];
                    if (obj == null) {
                        table.Add(kvp.Key, kvp.Value);
                    } else if (obj is ArrayList) {
                        var list = kvp.Value as ArrayList;
                        if (list != null) {
                            (obj as ArrayList).AddRange(list);
                        } else {
                            (obj as ArrayList).Add(kvp.Value);
                        }
                    } else {
                        var r = new ArrayList { obj, kvp.Value };
                        table[kvp.Key] = r;
                    }
                }
                return HandlerArray(ParseObjPath(table));

            }
            if (queryString.IndexOf("=", StringComparison.Ordinal) < 0) return queryString;
            var tmp = queryString.Split('=');
            if (tmp.Length != 2 || string.IsNullOrWhiteSpace(tmp[0]) || string.IsNullOrWhiteSpace(tmp[1])) return null;
            var isArray = false;
            var name = server.UrlDecode(tmp[0]);
            // ReSharper disable once PossibleNullReferenceException
            if (name.EndsWith("[]")) {
                name = name.Substring(0, name.Length - 2);
                isArray = true;
            }
            var index = name.IndexOf('[');
            if (index > 0) {
                name = "[" + name.Insert(index, "]");
            }

            var result = new KeyValuePair<string, object>(
                name,
                isArray ? new ArrayList() { ParseValue(server.UrlDecode(tmp[1])) } :
                    ParseValue(server.UrlDecode(tmp[1])));
            return result;
        }
        
        #region ParseQueryToJson2
        static object HandlerArray(object data) {
            var arr = data as ArrayList;
            if (arr != null) {
                for (var i = 0; i < arr.Count; i++) {
                    arr[i] = HandlerArray(arr[i]);
                }
                return arr;
            }
            var table = data as Hashtable;
            if (table == null) return data;
            //处理数组
            var regNum = new Regex("^\\d+$", RegexOptions.Compiled);
            if (!regNum.IsMatch(table.Keys.Join(""))) {
                var newTable = new Hashtable();
                foreach (var key in table.Keys) {
                    newTable[key] = HandlerArray(table[key]);
                }
                return newTable;
            }
            var arrList = new ArrayList();
            foreach (var key in table.Keys) {
                arrList.Add(table[key]);
            }
            return arrList;
        }
        static object ParseObjPath(Hashtable table) {
            var result = new Hashtable();
            foreach (var k in table.Keys) {
                var name = k.ToString();
                var value = table[name];
                var index = name.IndexOf('[');
                if (index >= 0) {
                    var reg = new Regex("\\[(.+?)\\]", RegexOptions.Compiled);
                    var ms = reg.Matches(name);
                    if (ms.Count <= 0) continue;
                    var m = ms[0];
                    var newName = m.Groups[1].Value;
                    if (ms.Count == 1) {
                        result[newName] = value;
                    } else {
                        var newTable = result[newName] as Hashtable ?? new Hashtable();
                        if (ms.Count == 2) {
                            newTable.Add(ms[1].Groups[1].Value, value);
                        } else {
                            var lastPath = name.Substring(m.Length);
                            newTable.Add(lastPath, value);
                        }
                        result[newName] = ParseObjPath(newTable);
                    }
                } else {
                    var tt = result[name];
                    if (tt == null) {
                        result[name] = value;
                    } else if (value is ArrayList) {
                        if (tt is ArrayList) {
                            (value as ArrayList).AddRange(tt as ArrayList);
                        } else if (tt is Hashtable) {
                            var tmpTable = tt as Hashtable;
                            foreach (var tk in tmpTable.Keys) {
                                (value as ArrayList).Add(tmpTable[tk]);
                            }
                        } else {
                            (value as ArrayList).Add(tt);
                        }
                        result[name] = value;
                    } else if (tt is ArrayList) {
                        if (value is Hashtable) {
                            var tmpTable = value as Hashtable;
                            foreach (var tk in tmpTable.Keys) {
                                (tt as ArrayList).Add(tmpTable[tk]);
                            }
                        } else {
                            (tt as ArrayList).Add(value);
                        }
                        result[name] = tt;
                    } else if (tt is Hashtable && value is Hashtable) {
                        var t1 = tt as Hashtable;
                        var t2 = value as Hashtable;
                        var newTable = new Hashtable();
                        foreach (var item in t1.Keys) {
                            newTable[item] = t1[item];
                        }
                        foreach (var item in t2.Keys) {
                            newTable[item] = t2[item];
                        }
                        result[name] = newTable;
                    } else {
                        Log.Error("table=" + JsonConvert.SerializeObject(table));
                        Log.Error("result=" + JsonConvert.SerializeObject(result));
                        Log.Error("tt=" + JsonConvert.SerializeObject(tt));
                        Log.Error("value=" + JsonConvert.SerializeObject(value));
                        throw new Exception("不知道是什么	情况");
                    }
                }
            }
            return result;
        }
        static object ParseValue(string queryString) {
            if (string.IsNullOrWhiteSpace(queryString)) {
                return null;
            }
            if (queryString.StartsWith("?")) {
                return ParseQueryToJson(queryString.Substring(1));
            }
            if (string.CompareOrdinal("true", queryString) == 0) return true;
            if (string.CompareOrdinal("false", queryString) == 0) return false;

            var intNumReg = new Regex("^-{0,1}\\d+$", RegexOptions.Compiled);
            if (intNumReg.IsMatch(queryString)) {
                if (queryString.StartsWith("-") == false && queryString.Length > 1 && queryString.StartsWith("0")) return queryString;
                if (queryString.StartsWith("-") && queryString.Length > 2 && queryString.Substring(1, 1) == "0") return queryString;
                if ((queryString.StartsWith("-") && queryString.Length < 11) || queryString.Length < 10)
                    return int.Parse(queryString);
                return queryString;
            }
            var decimalReg = new Regex("^-{0,1}\\d+\\.{1}\\d+$", RegexOptions.Compiled);
            if (decimalReg.IsMatch(queryString)) {
                return decimal.Parse(queryString);
            }
            if (IsDate(queryString) || IsDateTime(queryString)) {
                DateTime date;
                if (DateTime.TryParse(queryString, out date)) {
                    return date;
                }
            }
            return queryString;
        }
        #endregion

        /// <summary>
        /// 是否为日期型字符串
        /// </summary>
        /// <param name="StrSource">日期字符串(2008-05-08)</param>
        /// <returns></returns>
        public static bool IsDate(string StrSource) {
            return Regex.IsMatch(StrSource, @"^((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-9]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$");
        }
        /// <summary>
        /// 是否为日期+时间型字符串
        /// </summary>
        /// <param name="StrSource"></param>
        /// <returns></returns>
        public static bool IsDateTime(string StrSource) {
            return Regex.IsMatch(StrSource, @"^(((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-)) (20|21|22|23|[0-1]?\d):[0-5]?\d:[0-5]?\d)$ ");
        }
        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="queryString"></param>
        /// <returns></returns>
        public static NameValueCollection ParseQuery(string queryString) {
            var nv = new NameValueCollection();
            if (string.IsNullOrWhiteSpace(queryString)) return nv;
            if (queryString.StartsWith("?")) queryString = queryString.Substring(1);
            var pars = queryString.Split('&')
                .Where(item => item.IndexOf('=') > 0)
                .Select(item => item.Split('='))
                .Where(item => item.Length >= 2 && !string.IsNullOrWhiteSpace(item[0]) && !string.IsNullOrWhiteSpace(item[1]));
            foreach (var item in pars) {
                nv.Add(
                    System.Web.HttpContext.Current.Server.UrlDecode(item[0]),
                    // ReSharper disable once AssignNullToNotNullAttribute
                    System.Web.HttpContext.Current.Server.UrlDecode(item[1]));

            }
            return nv;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static NameValueCollection ParseQuery(this Uri uri) {
            return ParseQuery(uri.Query);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static NameValueCollection ParseQuery(this UriBuilder uri) {
            return ParseQuery(uri.Query);
        }
    }
}
